package com.example.mall.aspect;

import com.alibaba.fastjson.JSON;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * 功能:
 * 作者: 沙琪马
 * 日期: 2025/2/25 20:38
 */
@Component
@Aspect
public class TakeTimeAspect {

    private static final Logger log = LoggerFactory.getLogger(TakeTimeAspect.class);
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");

    // 统一线程本地变量
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    ThreadLocal<Long> endTime = new ThreadLocal<>();

    /**
     * 定义切点：带有@TakeTime注解的方法
     */
    @Pointcut("@annotation(com.example.mall.annotation.TakeTime)")
    public void takeTime() {

    }

    /**
     * 方法执行前
     *
     * @param joinPoint 连接点
     */
    @Before("takeTime()")
    public void doBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] params = joinPoint.getArgs();

        // 记录开始时间
        startTime.set(System.currentTimeMillis());
        String startDateTime = sdf.format(new Date(startTime.get()));
        log.info("【方法开始】==> 方法名称: {}, 开始时间: {}, 参数: {}",
                methodName, startDateTime, JSON.toJSONString(params));

        // 记录请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String requestId = UUID.randomUUID().toString();
        log.info("【请求信息】==> 请求ID: {}, URL: {}, 方法: {}, 参数: {}",
                requestId, request.getRequestURL().toString(),
                request.getMethod(), JSON.toJSONString(params));
    }

    /**
     * 方法执行后
     *
     * @param joinPoint 连接点
     * @param ret      返回值
     */
    @AfterReturning(pointcut = "takeTime()", returning = "ret")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {
        // 记录结束时间
        endTime.set(System.currentTimeMillis());
        String endDateTime = sdf.format(new Date(endTime.get()));
        log.info("【方法结束】==> 方法名称: {}, 结束时间: {}, 执行时长: {}ms, 返回值: {}",
                joinPoint.getSignature().getName(),
                endDateTime,
                endTime.get() - startTime.get(),
                JSON.toJSONString(ret));

        // 清除线程本地变量
        startTime.remove();
        endTime.remove();
    }

    /**
     * 方法执行异常
     *
     * @param joinPoint 连接点
     * @param ex       异常
     */
    @AfterThrowing(pointcut = "takeTime()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        endTime.set(System.currentTimeMillis());
        String endDateTime = sdf.format(new Date(endTime.get()));
        log.error("【方法异常】==> 方法名称: {}, 异常时间: {}, 异常信息: {}, 执行时长: {}ms",
                joinPoint.getSignature().getName(),
                endDateTime,
                ex.getMessage(),
                endTime.get() - startTime.get());

        // 清除线程本地变量
        startTime.remove();
        endTime.remove();
    }

    /**
     * 格式化日期
     *
     * @param date 日期对象
     * @return 格式化后的日期字符串
     */
    private String formatDateTime(Date date) {
        return sdf.format(date);
    }
}
